package com.jtw.sys.anno;

import com.esotericsoftware.kryo.Kryo;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jtw.common.bean.vo.ListWrapper;
import com.jtw.common.util.DeepCopyUtil;
import com.jtw.common.util.DeviceUtil;
import com.jtw.common.util.IpUtil;
import com.jtw.sys.constants.Constant;
import com.jtw.sys.model.log.TSysAccessLog;
import com.jtw.sys.service.log.SysAccessServiceImpl;
import com.jtw.sys.vo.user.SysUserInfoBaseVo;
import eu.bitwalker.useragentutils.Browser;
import eu.bitwalker.useragentutils.OperatingSystem;
import eu.bitwalker.useragentutils.UserAgent;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.UnavailableSecurityManagerException;
import org.apache.shiro.subject.Subject;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;

/**
 * DESCRIPT: 日志自动插入注解
 *
 * @author cjsky666
 * @date 2019/1/3 16:29
 */
@Component
@Aspect
@Slf4j
public class SysAccsessLogAnnoCheck {
    /**
     * 系统访问白名单，不需要记录到访问数据库的非公开接口权限
     */
    private static List<String> whiteURIs = Arrays.asList(
            "/sys/information/modifySysInformation"
            );
    private static Object lock = new Object();//定义一个存储锁，防止task在进行增删改的时候出现task线程操作数据的错误
    private static List<TSysAccessLog> sysAccessLogs  = new ArrayList<TSysAccessLog>();
    private static Date[]  dates = new Date[2];
    @Autowired
    private SysAccessServiceImpl sysAccessService;

    //如果是After的化会导致 验证码请求的日志记录写入失败
    @Before(("execution(* com.jtw.sys.controller..*.*(..)) && @annotation(apiOperation)"))
    public void checkAccsessLog(JoinPoint joinPoint,ApiOperation apiOperation) {
        HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
        log.debug("=======进入到反射日志记录=======");
        if(whiteURIs.indexOf(request.getRequestURI())>-1){
            log.debug(request.getRequestURI()+"当前路径在记录白名单，不需要存储访问记录");
            return;
        }
        log.debug("当前请求的getRequestURI为"+request.getRequestURI());
        log.debug("当前请求的"+Constant.KEY_CSRF_TOKEN+"为"+request.getHeader(Constant.KEY_CSRF_TOKEN));
        log.debug("当前请求的getMethod为"+request.getMethod());
        log.debug("当前请求的getQueryString为"+request.getQueryString());
        log.debug("当前请求的getParameterMap为"+request.getParameterMap());
        log.debug("当前请求的getContentType为"+request.getContentType());
        log.debug("当前请求的sessionid为"+request.getRequestedSessionId());
        log.debug("当前请求的PathInfo为"+request.getPathInfo());
        log.debug("当前请求的PathTranslated为"+request.getPathTranslated());
        log.debug("当前请求的ContextPath为"+request.getContextPath());
        log.debug("当前请求的getAuthType为"+request.getAuthType());
        log.debug("当前请求的getRemoteUser为"+request.getRemoteUser());
        log.debug("当前请求的getServletPath为"+request.getServletPath());
        log.debug("当前请求的getCharacterEncoding为"+request.getCharacterEncoding());
        log.debug("当前请求的getLocalAddr为"+request.getLocalAddr());
        log.debug("当前请求的getLocalName为"+request.getLocalName());
        log.debug("当前请求的getProtocol为"+request.getProtocol());
        log.debug("当前请求的getRemoteHost为"+request.getRemoteHost());
        log.debug("当前请求的getRemoteAddr为"+request.getRemoteAddr());
        log.debug("当前请求的getScheme为"+request.getScheme());
        log.debug("当前请求的getServerName为"+request.getServerName());
        log.debug("当前请求的getCookies为"+request.getCookies());
        log.debug("当前请求的getLocale为"+request.getLocale());
        log.debug("当前请求的getLocalPort为"+request.getLocalPort());
        log.debug("当前请求的getParameterNames为"+request.getParameterNames());
        log.debug("当前请求的getRemotePort为"+request.getRemotePort());
        log.debug("当前请求的getServerPort为"+request.getServerPort());
        log.debug("当前请求的getHeader为"+request.getHeader("User-Agent"));
        String urlName = apiOperation.value();
        saveSysAccessLog(request,urlName);
    }

    public void saveSysAccessLog(HttpServletRequest request, String urlName){

        TSysAccessLog sysAccessLog = new TSysAccessLog();

        UserAgent userAgent = DeviceUtil.getByRequest(request);
        Browser browser = DeviceUtil.getBrowserByUserAgent(userAgent);
        OperatingSystem operatingSystem = DeviceUtil.getOperatingSystemByUserAgent(userAgent);

        if (userAgent!=null){
            log.debug("浏览器版本:"+userAgent.getBrowserVersion());
            if(userAgent.getBrowserVersion()!=null){
                sysAccessLog.setBrowserVersion(userAgent.getBrowserVersion().toString());
            }else{
                sysAccessLog.setBrowserVersion("未检测到浏览器版本");
            }
        }else{
            sysAccessLog.setBrowserVersion("未检测到浏览器版本");
        }

        if(browser!=null){
            log.debug("浏览器名:"+browser.getName());
            log.debug("浏览器类型:"+browser.getBrowserType());
            log.debug("浏览器家族:"+browser.getGroup());
            log.debug("浏览器生产厂商:"+browser.getManufacturer());
            log.debug("浏览器使用的渲染引擎:"+browser.getRenderingEngine());
            log.debug("操作系统名:"+operatingSystem.getName());
            log.debug("操作系统类型:"+operatingSystem.getDeviceType());
            log.debug("操作系统组织名称:"+operatingSystem.getGroup());
            sysAccessLog.setBrowserType(browser.getBrowserType().toString());
            sysAccessLog.setBrowserName(browser.getGroup().toString());
        }else{
            sysAccessLog.setBrowserType("未检测到浏览器类型");
            sysAccessLog.setBrowserName("未检测到浏览器名称");
        }

        if(operatingSystem!=null){
            log.debug("访问设备类型:"+operatingSystem.getDeviceType());
            log.debug("操作系统家族:"+operatingSystem.getGroup());
            log.debug("操作系统生产厂商:"+operatingSystem.getManufacturer());
            sysAccessLog.setOs(operatingSystem.getName());
        }else{
            sysAccessLog.setOs("未检测到设备类型");
        }

        try {
            if(request.getSession()!=null&&request.getSession().getAttribute(Constant.USER_PRIMARY_KEY)!=null){
                sysAccessLog.setUserId((long)request.getSession().getAttribute(Constant.USER_PRIMARY_KEY));
                sysAccessLog.setUserName((String)request.getSession().getAttribute(Constant.KEY_USER_USER_NAME));
            }else{
                sysAccessLog.setUserId(0L);
                sysAccessLog.setUserName("");
            }
        }catch (UnavailableSecurityManagerException e){
            log.warn("异常检查：未登录导致没有用户信息，不需处理"+e.getMessage());
        }
        sysAccessLog.setUrl(request.getRequestURI());
        sysAccessLog.setUrlName(urlName);
        sysAccessLog.setCreateTime(new Date());
        sysAccessLog.setAddress(IpUtil.getIpFromAttribution(request.getRemoteAddr()));
        sysAccessLog.setHttpMethod(request.getMethod());
        sysAccessLog.setIp(request.getRemoteAddr());
        sysAccessLog.setRenderEngine(browser.getRenderingEngine().toString());
        Long start = Calendar.getInstance().getTimeInMillis();
        sysAccessLog.setPort(String.valueOf(request.getRemotePort()));
        if(request.getMethod().equals(HttpMethod.GET.toString())){
            sysAccessLog.setParams(request.getQueryString());
        }else if(request.getMethod().equals(HttpMethod.POST.toString())){
            String t = null;
            try {
                t = new ObjectMapper().writeValueAsString(request.getParameterMap());
            } catch (JsonProcessingException e) {
                log.error("POST访问参数解析错误"+e.getMessage());
            }
            sysAccessLog.setParams(t);
        }
        synchronized(lock){
            sysAccessLogs.add(sysAccessLog);
            if(sysAccessLogs.size()>40) {
                sysAccessService.saveAll(sysAccessLogs);
                sysAccessLogs.clear();//40条保存一次，然后清空 根据实际情况调整
                lock.notifyAll();
            }
        }
    }
}
